package avriServer.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

import avriServer.channel.Channel;
import avriServer.channel.NonexistantChannelException;
import avriServer.channel.NonexistantParentChannelException;
import avriServer.channel.WrongChannelNameException;
import avriServer.client.Worker;
import avriServer.obj.Action;
import avriServer.obj.Cleaner;
import avriServer.obj.CleanerChannel;



public class AvriServer extends Thread{
		
	public static final byte[] EOL = {(byte)'\n' };
	public final static HashMap<String, Integer> COMET_TYPE = new HashMap<String, Integer>();
	static {
		COMET_TYPE.put("Webkit", 1);
		COMET_TYPE.put("Presto", 2);
		COMET_TYPE.put("Trident", 3);
		COMET_TYPE.put("Other", 0);
	}
	private static Boolean alreadyCreated = false;
	private Cleaner cleaner ;
	private CleanerChannel cleanerChannel ;
	
	private int serverPort = 9090;
	private String defaultChannel = null;	
	private String characterSession = "azertyuiopmlkjhgfdsqwxcvbn1234567890AZERTYUIOPMLKJHGFDSQWXCVBN" ;
	private String characterPublicId = "azertyuiopmlkjhgfdsqwxcvbn1234567890AZERTYUIOPMLKJHGFDSQWXCVBN" ;
	private String cometName = "MooComet" ;
	private int sizePublicId = 10;
	private int sizeSession = 25;
	private long keepAlive = 50000 ;
	private long keepAliveChannel = 50000 ;
	private boolean isStarted = false ;
	private String commandPackageName;
	
	public AvriServer (int _serverPort) {
		this("", _serverPort);
	}
	
	public AvriServer (String _commandPackageName, int _serverPort) {
		if (AvriServer.alreadyCreated) {
			throw new RuntimeException("Only one instance of AvriServer can run. Use different Channels if you need to use different applications.");
		}
		if (_commandPackageName != "" && !_commandPackageName.substring(_commandPackageName.length()-1, _commandPackageName.length()).equals("."))
			_commandPackageName += ".";
		AvriServer.alreadyCreated = true ;
		Dispatcher.setServer(this);
		this.serverPort = _serverPort;
		this.commandPackageName = _commandPackageName;
	}
		
	/**
	 * @return the port the server will wait clients' requests
	 */
	public int getServerPort() {
		return serverPort;
	}

	/**
	 * @return the server default Channel
	 * default null
	 */
	public String getDefaultChannel() {
		return defaultChannel;
	}

	/**
	 * @param set the defaultChannel value. For exemple, this value can be used into a Connect command to automatically join the waiter to the defaultChannel.
	 * default null
	 */
	public void setDefaultChannel(String defaultChannel) {
		this.defaultChannel = defaultChannel;
	}
	
	/**
	 * Set the default authorized string used when the Dispatcher generate a Session.
	 * Default value "azertyuiopmlkjhgfdsqwxcvbn1234567890AZERTYUIOPMLKJHGFDSQWXCVBN"
	 * @param characterSession
	 */
	public void setCharacterSession(String characterSession) {
		this.characterSession = characterSession;
	}

	/**
	 * Default value "azertyuiopmlkjhgfdsqwxcvbn1234567890AZERTYUIOPMLKJHGFDSQWXCVBN"
	 * @return the characterPublicId
	 */
	public void setCharacterPublicId(String characterPublicId) {
		this.characterPublicId = characterPublicId;
	}

	/**
	 * Default value "azertyuiopmlkjhgfdsqwxcvbn1234567890AZERTYUIOPMLKJHGFDSQWXCVBN"
	 * @return the characterSession
	 */
	String getCharacterSession() {
		return this.characterSession ;
	}

	/**
	 * Set the default authorized string used when the Dispatcher generate a PublicId.
	 * Default value "azertyuiopmlkjhgfdsqwxcvbn1234567890AZERTYUIOPMLKJHGFDSQWXCVBN"
	 * @param characterPublicId
	 */
	String getCharacterPublicId() {
		return this.characterPublicId ;
	}

	/**
	 * Return the value of the CometName. Mainly used for opera
	 * Default value "MooComet"
	 */
	String getDefaultCometName() {
		return cometName;
	}

	/**
	 * set the cometName.
	 * Default value "MooComet"
	 * @param defaultCometName
	 */
	public void setDefaultCometName(String cometName) {
		this.cometName = cometName;
	}

	/**
	 * @return the size (number of characters) of the PublicId generated by the dispatcher.
	 */
	public int getSizePublicId() {
		return sizePublicId;
	}
	
	/**
	 * set the size (number of characters) of the PublicId generated by the dispatcher.
	 * default value 10
	 * @param sizePublicId
	 */
	public void setSizePublicId(int sizePublicId) {
		this.sizePublicId = sizePublicId;
	}


	/**
	 * @return the size (number of characters) of the session generated by the dispatcher.
	 * default value 25
	 */
	public int getSizeSession() {
		return sizeSession;
	}


	/**
	 * set the size (number of characters) of the session generated by the dispatcher.
	 * default value 25
	 * @param sizeSession
	 */
	public void setSizeSession(int sizeSession) {
		this.sizeSession = sizeSession;
	}

	/**
	 * @return the keepAlive Value
	 */
	public long getKeepAlive() {
		return keepAlive;
	}

	/**
	 * set the keepAlive value. Server Keep alive value MUST BE at least more than the double of the client keepAlive value.
	 * @param keepAlive
	 */
	public void setKeepAlive(long keepAlive) {
		this.keepAlive = keepAlive;
	}

	/**
	 * @return the keepAliveChannel value for Temporary Channel
	 */
	public long getKeepAliveChannel() {
		return keepAliveChannel;
	}

	/**
	 * set the keepAlive
	 * @param keepAliveChannel
	 */
	public void setKeepAliveChannel(long keepAliveChannel) {
		this.keepAliveChannel = keepAliveChannel;
	}

	public void stopServer () {
		this.isStarted = false ;
	}
	@Override
	public void run () {
		this.isStarted = true ;
		this.cleaner = new Cleaner();
		this.cleaner.start();
		this.cleanerChannel = new CleanerChannel();
		this.cleanerChannel.start();
		
		try{
			ServerSocket ss = new ServerSocket(this.serverPort);
			Socket s = new Socket () ;
			while(this.isStarted) {
				System.out.println("listen");
				s = ss.accept();
				new Thread(new Worker(s)).start();
			}
		}	
		catch(IOException e){
			e.printStackTrace();
			System.out.println("Error IO");
		}
	}
	
	/**
	 * @return the package command name
	 */
	public String getCommandPackageName() {
		return commandPackageName;
	}
}